
#include <cstdio>
#include <BCharEvent>
#include <BKeyEvent>
#include <BEvent>
#include <BEvent>
#include <BMouseEvent>
#include <BEvent>
#include <BCharEvent>
#include <BDragEvent>
#include <BTextEdit>
#include <BPainter>
#include <BCursor>
#include <BDrag>
#include <BSystem>
#include <BScreen>
#include "IWindow.h"
#include "member_BWidget.h"

using namespace BWE;

extern BList<IWindowHolder> app_windows;
#ifdef linux
extern Display*				app_display;
#endif

#ifdef linux

#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/keysymdef.h>

bool IWindow::create()
{
	screen = 0;
	display = 0;
	context = 0;
	style = 0;

	display = app_display;
	screen = XScreenOfDisplay(display, DefaultScreen(app_display));

	const char* title = boss->title().cstr();

	BRect rect = boss->rect();
	int x = rect.x();
	int y = rect.y();
	int width = rect.width();
	int height = rect.height();

	int attribList[]  = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };//{GLX_RGBA, GLX_DOUBLEBUFFER, None};
  	XVisualInfo* vinfo = glXChooseVisual(display, 0, attribList);
  	if (vinfo == 0)
  	{
  		XCloseDisplay(display);
  		display = 0;
    	perror("NO RGBA visual with double buffer");
    	return false;
  	}
 
 	IWindow* firstWindow = app_windows.first();
	if(firstWindow)
  		context = glXCreateContext(display, vinfo, firstWindow->context, GL_TRUE);
	else
  		context = glXCreateContext(display, vinfo, None, GL_TRUE);
  	if (context == 0)
  	{
  		XCloseDisplay(display);
  		display = 0;
    	perror("could not create rendering context");
    	return false;
    }
	
 	rootHwnd = DefaultRootWindow(display);
  	Visual* visual = vinfo->visual;//DefaultVisual(display, 0);
	
 	XSetWindowAttributes attribs;
 	attribs.colormap = XCreateColormap(display, rootHwnd, visual, AllocNone);
	attribs.background_pixmap = None;
	attribs.background_pixel = 0xf0808080; 
 	attribs.border_pixmap = None;
 	attribs.border_pixel = 0;
  	attribs.event_mask = CreateNotify\
	  	  				| DestroyNotify\
  						| ExposureMask\
  						| FocusChangeMask\
  						| KeyPressMask\
  						| KeyReleaseMask\
  						| PointerMotionMask\
  						| ButtonPressMask\
  						| ButtonReleaseMask\
  						| ButtonMotionMask\
  						| EnterWindowMask\
  						| LeaveWindowMask\
  						| StructureNotifyMask\
						| OwnerGrabButtonMask;

  	unsigned long attrs_mask = CWBackPixel | CWBackPixmap | CWBorderPixel | CWBorderPixmap | CWColormap | CWEventMask;
	if(widget_member(boss)->frameless)
	{
		attribs.override_redirect = true;
		attrs_mask |= CWOverrideRedirect;
	}

  	hwnd = XCreateWindow(display, rootHwnd, x, y, width, height, 0, vinfo->depth, InputOutput, visual, attrs_mask, &attribs);
	XStoreName(display, hwnd, boss->title().cstr());
	XSetIconName(display, hwnd, "BWindow");

	wmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", True);
	XSetWMProtocols(display, hwnd, &wmDeleteWindow, 1);

	memset(fontCursors, 0, sizeof(fontCursors));
	return true;
}
bool IWindow::destroy()
{
	for(int i = 0; i < 160; i++)
	{
		if(fontCursors[i])
			XFreeCursor(display, fontCursors[i]);
	}
	if (context)
		glXDestroyContext(display, context);
	if (hwnd)
		XDestroyWindow(display, hwnd);
	if (display)
		XCloseDisplay(display);
	display = 0;
	hwnd = 0;
	context = 0;
	return true;
}

bool IWindow::makeCurrent(bool yes)
{
	if(display)
	{
		if (yes)
		{
			glXMakeCurrent(display, hwnd, context);
		}
		else
		{
			glXMakeCurrent(display, 0, 0);
		}
	}
	return true;
}
bool IWindow::swapBuffers()
{
	if(display)
	{
		glXSwapBuffers(display, hwnd);
		XFlush(display);
	}
	return true;
}

bool IWindow::setWindowTitle(const BString& title)
{
	int ret = XStoreName(display, hwnd, title.cstr());
	return ret == 0;
}
BString IWindow::getWindowTitle()
{
	XWindowAttributes attribs;
	int ret = XGetWindowAttributes(display, hwnd, &attribs);
	return BString();
}

bool IWindow::setWindowIcon(const BIcon* icon)
{
	return false;
}
void IWindow::setFrameless(bool frameless)
{
 	XSetWindowAttributes attribs;
	attribs.override_redirect = True;
	unsigned long attrs_mask = CWOverrideRedirect;
	XChangeWindowAttributes(display, hwnd, attrs_mask, &attribs);
}

void IWindow::setWindowPos(const BPoint& pos)
{
	if(hwnd)
	{
		if(pos != this->getWindowPos())
		{
			XMoveWindow(display, hwnd, pos.x(), pos.y());
			BPoint retpos = this->getWindowPos();
			//printf("move window (%d, %d) : (%d, %d)\n", pos.x(), pos.y(), retpos.x(), retpos.y());
		}
	}
}
BPoint IWindow::getWindowPos()
{
	XWindowAttributes attribs;
	int ret = XGetWindowAttributes(display, hwnd, &attribs);
	return BPoint(attribs.x, attribs.y);
}

void IWindow::setWindowSize(const BSize& size)
{
	if(hwnd)
	{
		if (size != this->getWindowSize())
		{
			XResizeWindow(display, hwnd, size.width(), size.height());
		}
	}
}
BSize IWindow::getWindowSize()
{
	XWindowAttributes attribs;
	int ret = XGetWindowAttributes(display, hwnd, &attribs);
	return BSize(attribs.width, attribs.height);
}

bool IWindow::isMaximized()
{
	return false;
}
bool IWindow::isMinimized()
{
	return false;
}

void IWindow::showNormal()
{
	XMapWindow(display, hwnd);
}
void IWindow::showMaximized()
{
	int fullWidth = XWidthOfScreen(screen);
	int fullHeight = XHeightOfScreen(screen);
	XMoveResizeWindow(display, hwnd, 0, 0, fullWidth, fullHeight);
}
void IWindow::showMinimized()
{
	Status ret = XIconifyWindow(display, hwnd, 0);
}
void IWindow::showFullScreen()
{
	int fullWidth = XWidthOfScreen(screen);
	int fullHeight = XHeightOfScreen(screen);
	XMoveResizeWindow(display, hwnd, 0, 0, fullWidth, fullHeight);
}
void IWindow::hideWindow()
{
	XUnmapWindow(display, hwnd);
}

void IWindow::freshCursor()
{
	BImage* image = 0;
	CursorType type = Cursor_None;
	if (hoverCursor)
	{
		type = hoverCursor->type();
		image = hoverCursor->image(type);
	}
	if (image)
	{
		XUndefineCursor(display, hwnd);
		return;
	}
	if (hoverCursor)
		type = hoverCursor->type();
	unsigned int font_cursor_type = 0;
	switch (type)
	{
	case Cursor_None:
	case Cursor_Arrow:
		font_cursor_type = XC_arrow;
		break;
	case Cursor_Ibeam:
		font_cursor_type = XC_xterm;
		break;
	case Cursor_Cross:
		font_cursor_type = XC_X_cursor;
		break;
	case Cursor_Wait:
		font_cursor_type = XC_watch;
		break;
	case Cursor_Forbidden:
		font_cursor_type = XC_X_cursor;
		break;
	case Cursor_Hand:
		font_cursor_type = XC_hand2;
		break;
	case Cursor_Help:
		font_cursor_type = XC_question_arrow;
		break;
	case Cursor_UpArrow:
		font_cursor_type = XC_sb_up_arrow;
		break;
	case Cursor_SizeTop:
		font_cursor_type = XC_top_side;
		break;
	case Cursor_SizeLeft:
		font_cursor_type = XC_left_side;
		break;
	case Cursor_SizeRight:
		font_cursor_type = XC_right_side;
		break;
	case Cursor_SizeBottom:
		font_cursor_type = XC_bottom_side;
		break;
	case Cursor_SizeVer:
		font_cursor_type = XC_sb_v_double_arrow;
		break;
	case Cursor_SizeHor:
		font_cursor_type = XC_sb_h_double_arrow;
		break;
	case Cursor_SizeLeftTop:
		font_cursor_type = XC_top_left_corner;
		break;
	case Cursor_SizeRightBottom:
		font_cursor_type = XC_bottom_right_corner;
		break;
	case Cursor_SizeLeftBottom:
		font_cursor_type = XC_bottom_left_corner;
		break;
	case Cursor_SizeRightTop:
		font_cursor_type = XC_top_right_corner;
		break;
	case Cursor_SizeAll:
		font_cursor_type = XC_fleur;
		break;
	default:
		break;
	}
	if(fontCursors[font_cursor_type] == 0)
	{
		fontCursors[font_cursor_type] = XCreateFontCursor(display, font_cursor_type);
	}
	Cursor cursor = fontCursors[font_cursor_type];
	XDefineCursor(display, hwnd, cursor);
	XFlush(display);
}

bool IWindow::procEvent(const XEvent& event)
{
	switch(event.type)
	{
	case ReparentNotify:
	{
		const XReparentEvent& reparent = (const XReparentEvent&)event;
		break;
	}
	case CreateNotify:
	{
		const XCreateWindowEvent& createEvent = (const XCreateWindowEvent&)event;
		widget_member(boss)->syncWindowPos(BPoint(createEvent.x, createEvent.y));
		widget_member(boss)->syncWindowSize(BSize(createEvent.width, createEvent.height));
		break;
	}
	case DestroyNotify:
	{
		const XDestroyWindowEvent& destroyEvent = (const XDestroyWindowEvent&)event;
		BEvent closeEvent(Event_Close);
		widget_member(boss)->procEvent(closeEvent);
		break;
	}
	case ClientMessage:
	{
		if (event.xclient.data.l[0] == wmDeleteWindow)
		{
			BEvent closeEvent(Event_Close);
			widget_member(boss)->procEvent(closeEvent);
		}
		break;
	}
	case Expose:
	{
		//refresh = true;
		break;
	}
	case ConfigureNotify:
	{
		const XConfigureEvent& configEvent = (const XConfigureEvent&)event;
		widget_member(boss)->syncWindowPos(BPoint(configEvent.x, configEvent.y));
		widget_member(boss)->syncWindowSize(BSize(configEvent.width, configEvent.height));
		break;
	}
	case FocusIn:
	{
		focusing = true;
		BEvent focusEvent(Event_Focus);
		focusEvent.setValue(true);
		widget_member(boss)->procEvent(focusEvent);
		break;
	}
	case FocusOut:
	{
		focusing = false;
		BEvent focusEvent(Event_Focus);
		focusEvent.setValue(false);
		widget_member(boss)->procEvent(focusEvent);
		break;
	}
	case KeyPress:
	{
		XKeyEvent xkevent = (const XKeyEvent&)event;
		KeySym keysym = 0;
		char buf[8]={0};
		int len = XLookupString(&xkevent, buf, sizeof(buf), &keysym, NULL);
		Key key = filterKey(keysym);
		keyStates[key] = true;
		if (focusWidget)
		{
			bool ctrl = iscntrl(buf[0]);
			if(!ctrl && buf[len] == 0)
			{
				BCode code = buf;
				const char* ustr = code.str();
				BCharEvent charEvent(code);
				widget_member(focusWidget)->procEvent(charEvent);
			}
			BKeyEvent keyEvent(Event_KeyDown, key);
			widget_member(focusWidget)->procEvent(keyEvent);
		}
		break;
	}
	case KeyRelease:
	{
		XKeyEvent xkevent = (const XKeyEvent&)event;
		KeySym keysym = 0;
		char buf[8]={0};
		int len = XLookupString(&xkevent, buf, sizeof(buf), &keysym, NULL);
		Key key = filterKey(keysym);
		keyStates[key] = false;
		if (focusWidget)
		{
			BKeyEvent keyEvent(Event_KeyUp, key);
			widget_member(focusWidget)->procEvent(keyEvent);
		}
		break;
	}
	case EnterNotify:
	{
		if(!mouse_pressed)
		{
			hoverWidget = boss;
			BMouseEvent mouseEvent(Event_MouseEnter);
			widget_member(boss)->procEvent(mouseEvent);
		}
		break;
	}
	case LeaveNotify:
	{
		if(!mouse_pressed)
		{
			if (hoverWidget)
			{
				BMouseEvent mouseEvent(Event_MouseLeave);
				widget_member(boss)->procEvent(mouseEvent);
				hoverWidget = 0;
			}
		}
		break;
	}
	case MotionNotify:
	{
		const XMotionEvent& motionEvent = (const XMotionEvent&)event;
		BPoint absolutePos(motionEvent.x, motionEvent.y);
		BPoint globalPos(motionEvent.x_root, motionEvent.y_root);
		BMouseEvent mouseEvent(Event_MouseMove, mouse_button);
		mouseEvent.setAbsolutePos(absolutePos);
		mouseEvent.setGlobalPos(globalPos);
		this->procMouseMove(mouseEvent);
		break;
	}
	case ButtonPress:
	{
		const XButtonEvent& buttonEvent = (const XButtonEvent&)event;
		BPoint absolutePos(buttonEvent.x, buttonEvent.y);
		BPoint globalPos(buttonEvent.x_root, buttonEvent.y_root);
		if(buttonEvent.button == Button4)
		{
			BMouseEvent mouseEvent(Event_MouseWheel, Button_Middle);
			mouseEvent.setGlobalPos(globalPos);
			mouseEvent.setAbsolutePos(absolutePos);
			mouseEvent.setWheel(1);
			this->procMouseWheel(mouseEvent);
		}
		else if(buttonEvent.button == Button5)
		{
			BMouseEvent mouseEvent(Event_MouseWheel, Button_Middle);
			mouseEvent.setGlobalPos(globalPos);
			mouseEvent.setAbsolutePos(absolutePos);
			mouseEvent.setWheel(-1);
			this->procMouseWheel(mouseEvent);
		}
		else
		{
			Button button = Button_None;
			if(buttonEvent.button == Button1)
				button =  Button_Left;
			if(buttonEvent.button == Button2)
				button =  Button_Middle;
			if(buttonEvent.button == Button3)
				button =  Button_Right;
			BMouseEvent mouseEvent(Event_MousePress, button);
			mouseEvent.setAbsolutePos(absolutePos);
			mouseEvent.setGlobalPos(globalPos);
			this->procMousePress(mouseEvent);

		}
		break;
	}
	case ButtonRelease:
	{
		const XButtonEvent& buttonEvent = (const XButtonEvent&)event;
		BPoint absolutePos(buttonEvent.x, buttonEvent.y);
		BPoint globalPos(buttonEvent.x_root, buttonEvent.y_root);
		Button button = Button_None;
		if(buttonEvent.button == Button1)
			button =  Button_Left;
		if(buttonEvent.button == Button2)
			button =  Button_Middle;
		if(buttonEvent.button == Button3)
			button =  Button_Right;
		BMouseEvent mouseEvent(Event_MouseRelease, button);
		mouseEvent.setAbsolutePos(absolutePos);
		mouseEvent.setGlobalPos(globalPos);
		this->procMouseRelease(mouseEvent);
		break;
	}
	case MapNotify:
	{
		const XMapEvent& mapEvent = (const XMapEvent&)event;
		break;
	}
	case UnmapNotify:
	{
		const XUnmapEvent& unmapEvent = (const XUnmapEvent&)event;
		break;
	}
	default:
		printf("unaccessed event : %d\n", event.type);
		break;
	}
	return true;
}
Key IWindow::filterKey(KeySym keysym)
{
	switch(keysym)
	{
	case XK_0:			return Key_0;
	case XK_1:			return Key_1;
	case XK_2:			return Key_2;
	case XK_3:			return Key_3;
	case XK_4:			return Key_4;
	case XK_5:			return Key_5;
	case XK_6:			return Key_6;
	case XK_7:			return Key_7;
	case XK_8:			return Key_8;
	case XK_9:			return Key_9;
	case XK_colon:		return Key_Colon;
	case XK_semicolon:	return Key_Semicolon;
	case XK_less:		return Key_Less;
	case XK_equal:		return Key_Equal;
	case XK_greater:	return Key_Greater;
	case XK_question:	return Key_Question;

	case XK_A:			return Key_A;
	case XK_B:			return Key_B;
	case XK_C:			return Key_C;
	case XK_D:			return Key_D;
	case XK_E:			return Key_E;
	case XK_F:			return Key_F;
	case XK_G:			return Key_G;
	case XK_H:			return Key_H;
	case XK_I:			return Key_I;
	case XK_J:			return Key_J;
	case XK_K:			return Key_K;
	case XK_L:			return Key_L;
	case XK_M:			return Key_M;
	case XK_N:			return Key_N;
	case XK_O:			return Key_O;
	case XK_P:			return Key_P;
	case XK_Q:			return Key_Q;
	case XK_R:			return Key_R;
	case XK_S:			return Key_S;
	case XK_T:			return Key_T;
	case XK_U:			return Key_U;
	case XK_V:			return Key_V;
	case XK_W:			return Key_W;
	case XK_X:			return Key_X;
	case XK_Y:			return Key_Y;
	case XK_Z:			return Key_Z;

	case XK_bracketleft:	return Key_LBracket;
	case XK_backslash:		return Key_Backslash;
	case XK_bracketright:	return Key_RBracket;
	case XK_asciicircum:	return Key_None;
	case XK_underscore:		return Key_Underscore;
	case XK_grave:			return Key_None;

	case XK_a:			return Key_A;
	case XK_b:			return Key_B;
	case XK_c:			return Key_C;
	case XK_d:			return Key_D;
	case XK_e:			return Key_E;
	case XK_f:			return Key_F;
	case XK_g:			return Key_G;
	case XK_h:			return Key_H;
	case XK_i:			return Key_I;
	case XK_j:			return Key_J;
	case XK_k:			return Key_K;
	case XK_l:			return Key_L;
	case XK_m:			return Key_M;
	case XK_n:			return Key_N;
	case XK_o:			return Key_O;
	case XK_p:			return Key_P;
	case XK_q:			return Key_Q;
	case XK_r:			return Key_R;
	case XK_s:			return Key_S;
	case XK_t:			return Key_T;
	case XK_u:			return Key_U;
	case XK_v:			return Key_V;
	case XK_w:			return Key_W;
	case XK_x:			return Key_X;
	case XK_y:			return Key_Y;
	case XK_z:			return Key_Z;

	case XK_BackSpace:	return Key_Back;
	case XK_Tab:		return Key_Tab;
	case XK_Return:		return Key_Enter;
	case XK_Escape:		return Key_Escape;
	case XK_Delete:		return Key_Delete;

	case XK_Home:		return Key_Home;
	case XK_Left:		return Key_Left;
	case XK_Up:			return Key_Up;
	case XK_Right:		return Key_Right;
	case XK_Down:		return Key_Down;
	case XK_Page_Up:	return Key_PageUp;
	case XK_Page_Down:	return Key_PageDown;
	case XK_End:		return Key_End;

	case XK_Shift_L:	return Key_LShift;
	case XK_Shift_R:	return Key_RShift;
	case XK_Control_L:	return Key_LCtrl;
	case XK_Control_R:	return Key_RCtrl;
	case XK_Alt_L:		return Key_LAlt;
	case XK_Alt_R:		return Key_RAlt;
	case XK_Num_Lock:	return Key_NumLock;
	case XK_Caps_Lock:	return Key_CapsLock;

	default:
		break;
	}
	return Key_None;
}
#endif
